// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
// ====================
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
// ... with NTL restriction stating that:
// No trademark license is granted to use the trade names, trademarks, service
// marks, or product names of Contributor, except as required to fulfill notice
// requirements defined in MIT License.

import Foundation

// MARK: - SessionCoreProtocol

public protocol SessionCoreProtocol: AnyObject {
  associatedtype State: IMEStateProtocol
  associatedtype Handler: InputHandlerCoreProtocol
    where Handler.State == Handler.Session.State, Handler.State == State
  /// 僅用來決定 UI 面板先照顧哪個 Session 用，不宜用來判斷 isActivated。
  var id: UUID { get }
  /// 用以記錄當前輸入法狀態的變數。(有 DidSet)
  var state: State { get set }
  var isASCIIMode: Bool { get }
  var clientMitigationLevel: Int { get }
  var ui: SessionUIProtocol? { get }
  func updateCompositionBufferDisplay()
  func performUserPhraseOperation(addToFilter: Bool) -> Bool
  @discardableResult
  func updateVerticalTypingStatus() -> CGRect

  func toggleCandidateUIVisibility(_ newValue: Bool, refresh: Bool)
  func commit(text: String, clearDisplayBeforeCommit: Bool)
  func showTooltip(
    _ string: String?,
    colorState: TooltipColorState,
    duration: Double
  )
  func getMitigatedState(_ givenState: State) -> State

  /// 針對傳入的新狀態進行調度、且將當前會話控制器的狀態切換至新狀態。
  ///
  /// 先將舊狀態單獨記錄起來，再將新舊狀態作為參數，
  /// 根據新狀態本身的狀態種類來判斷交給哪一個專門的函式來處理。
  /// - Remark: ⚠️ 任何在這個函式當中被改變的變數均不得是靜態 (Static) 變數。
  /// 針對某一個客體的 deactivateServer() 可能會在使用者切換到另一個客體應用
  /// 且開始敲字之後才會執行。這個過程會使得不同的 SessionCtl 副本之間出現
  /// 不必要的互相干涉、打斷彼此的工作。
  /// - Note: 本來不用這麼複雜的，奈何 Swift Protocol 不允許給參數指定預設值。
  /// - Parameter newState: 新狀態。
  func switchState(_ newState: State, caller: StaticString, line: Int)
}

extension SessionCoreProtocol {
  public func switchState(_ newState: State, caller: StaticString = #function, line: Int = #line) {
    switchState(newState, caller: caller, line: line)
  }

  public var isCurrentSession: Bool {
    id == ui?.currentSessionID
  }

  public func toggleCandidateUIVisibility(_ newValue: Bool, refresh: Bool = true) {
    toggleCandidateUIVisibility(newValue, refresh: refresh)
  }

  public func commit(text: String, clearDisplayBeforeCommit: Bool = false) {
    commit(text: text, clearDisplayBeforeCommit: clearDisplayBeforeCommit)
  }

  public func showTooltip(
    _ string: String?,
    colorState: TooltipColorState = .normal,
    duration: Double = 0
  ) {
    showTooltip(string, colorState: colorState, duration: duration)
  }
}

// MARK: - InputHandlerCoreProtocol

public protocol InputHandlerCoreProtocol: AnyObject {
  associatedtype State: IMEStateProtocol
  associatedtype Session: SessionCoreProtocol & CtlCandidateDelegate
    where Session.State == Session.Handler.State, Session.State == State
}
